using System;
using System.Reflection;
using Boo.Lang.Compiler.Ast;
using Boo.Lang.Compiler.Steps;

namespace Mite.DSL
{
	/// <summary>
	/// Transforms boo macro statements and method invocations that start with a lower case letter
	/// to starting with an upper case level if the base class associated with this compiler step
	/// contains a property or method matching the upper cased name of the boo node.
	/// </summary>
	public class StartsWithLowerNamingConventionsToStartsWithUpperCompilerStep : AbstractVisitorCompilerStep
	{
		/// <summary>
		/// Create an instance of StartsWithLowerNamingConventionsToStartsWithUpperCompilerStep
		/// </summary>
		public StartsWithLowerNamingConventionsToStartsWithUpperCompilerStep(Type baseClass)
		{
			if (baseClass == null)
				throw new ArgumentNullException("baseClass");
			this.baseClass = baseClass;

			BindingFlags memberConsiderations = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

			properties = baseClass.GetProperties(memberConsiderations);
			methods = baseClass.GetMethods(memberConsiderations);
		}

		/// <summary>
		/// Transform macro statements.
		/// </summary>
		public override void OnMacroStatement(MacroStatement node)
		{
			if (ShouldTransform(node))
				node.Name = Transform(node.Name);
			base.OnMacroStatement(node);
		}

		/// <summary>
		/// Transform reference expressions.
		/// </summary>
		public override void OnReferenceExpression(ReferenceExpression node)
		{
			if (ShouldTransform(node))
				node.Name = Transform(node.Name);
			base.OnReferenceExpression(node);
		}

		/// <summary>
		/// Returns <c>true</c> if the node's name begins with a lower case letter
		/// and would match a member of <see cref="baseClass"/> if it began with an upper case letter.
		/// </summary>
		protected bool ShouldTransform(MacroStatement node)
		{
			if (StartsWithLower(node.Name))
			{
				string uppercaseName = Transform(node.Name);
				bool isMethod = Array.Exists(methods, delegate(MethodInfo method) { return method.Name.Equals(uppercaseName); });

				return isMethod;
			}
			return false;
		}

		/// <summary>
		/// Returns <c>true</c> if the node's name begins with a lower case letter
		/// and would match a member of <see cref="baseClass"/> if it began with an upper case letter.
		/// </summary>
		protected bool ShouldTransform(ReferenceExpression node)
		{
			if (StartsWithLower(node.Name))
			{
				string uppercaseName = Transform(node.Name);

				bool isProperty = Array.Exists(properties,
				                               delegate(PropertyInfo property) { return property.Name.Equals(uppercaseName); });
				return isProperty;
			}
			return false;
		}

		private string Transform(string name)
		{
			char firstChar = Char.ToUpper(name[0]);
			return firstChar + name.Substring(1);
		}

		private bool StartsWithLower(string s)
		{
			return Char.IsLower(s[0]);
		}

		/// <summary>
		/// Run the compiler step.
		/// </summary>
		public override void Run()
		{
			Visit(CompileUnit);
		}

		private readonly Type baseClass;
		private readonly MethodInfo[] methods;
		private readonly PropertyInfo[] properties;
	}
}